//////////////////////////////////////////////////////////////////////////////////////////////
// Mini dump
// Based on : http://www.codeproject.com/tools/minidump.asp
// VC7: ships with updated headers
#include "dbghelp.h"
#include "tchar.h"

// based on dbghelp.h
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess, DWORD dwPid, HANDLE hFile, MINIDUMP_TYPE DumpType,
									CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
									CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
									CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam
									);

class MiniDumper
{
public:
	static TCHAR m_szAppName[_MAX_PATH];
	static HWND m_hWnd;

	static BOOL GenerateDumpFilename(LPTSTR pszDumpPath, LPTSTR pszBuffer, DWORD cbBuffer)
	{
		// Create filename
		SYSTEMTIME stTime;
		GetLocalTime(&stTime); 

		// Filename is composed like this, to avoid collisions;
		// <DumpPath>\DUMP-YYYYMMDD-HHMMSS-<PID>-<TID>.dmp
		TCHAR pszFilename[MAX_PATH];
		size_t cbFilename = sizeof(pszFilename) / sizeof(pszFilename[0]) - 1;
		unsigned iWritten = _sntprintf(pszFilename, cbFilename, _T("%sDUMP-%s-%04d%02d%02d-%02d%02d%02d-%ld-%ld.dmp"), pszDumpPath, m_szAppName, stTime.wYear,stTime.wMonth,stTime.wDay,stTime.wHour, stTime.wMinute, stTime.wSecond, GetCurrentProcessId(), GetCurrentThreadId());
		if(iWritten < 0 || (iWritten + 1) > cbBuffer)
		{
			// Either the local buffer or the output buffer was too small
			return FALSE;
		}

		_tcscpy(pszBuffer, pszFilename);

		return TRUE;
	}

	static LONG WINAPI TopLevelFilter( struct _EXCEPTION_POINTERS *pExceptionInfo )
	{
		LONG retval = EXCEPTION_CONTINUE_SEARCH;
		HWND hParent = m_hWnd;

		TCHAR szDumpPath[_MAX_PATH] = {0};

		// firstly see if dbghelp.dll is around and has the function we need
		// look next to the EXE first, as the one in System32 might be old 
		// (e.g. Windows 2000)
		HMODULE hDll = NULL;
		TCHAR szDbgHelpPath[_MAX_PATH];

		// Load the one in the current directory
		hDll = ::LoadLibrary( _T(".\\DBGHELP.DLL") );

		if (hDll == NULL)
		{
			if (GetModuleFileName( NULL, szDbgHelpPath, _MAX_PATH ))
			{
				TCHAR *pSlash = _tcsrchr( szDbgHelpPath, '\\' );
				if (pSlash)
				{
					_tcscpy( pSlash+1, _T("DBGHELP.DLL") );
					hDll = ::LoadLibrary( szDbgHelpPath );
				}
			}
		}

		if (hDll==NULL)
		{
			// load any version we can
			hDll = ::LoadLibrary( _T("DBGHELP.DLL") );
		}

		LPCTSTR szResult = NULL;

		if (hDll)
		{
			MINIDUMPWRITEDUMP pDump = (MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );
			if (pDump)
			{				
				TCHAR szScratch [_MAX_PATH] = {0};

				GenerateDumpFilename(_T("./"),szDumpPath,_MAX_PATH);

				// ask the user if they  want to save a dump file
				if (::MessageBox( hParent, _T("An unexpected exception occured, would you like to save a diagnostic file?"), m_szAppName, MB_YESNO|MB_TOPMOST )==IDYES)
				{
					// create the file
					HANDLE hFile = ::CreateFile( szDumpPath, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS,
						FILE_ATTRIBUTE_NORMAL, NULL );

					if (hFile!=INVALID_HANDLE_VALUE)
					{
						_MINIDUMP_EXCEPTION_INFORMATION ExInfo;

						ExInfo.ThreadId = ::GetCurrentThreadId();
						ExInfo.ExceptionPointers = pExceptionInfo;
						ExInfo.ClientPointers = NULL;

						// write the dump
						BOOL bOK = pDump( GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &ExInfo, NULL, NULL );
						if (bOK)
						{
							_stprintf( szScratch, _T("Saved dump file to '%s'"), szDumpPath );
							szResult = szScratch;
							retval = EXCEPTION_EXECUTE_HANDLER;
						}
						else
						{							
							_stprintf( szScratch, _T("Failed to save dump file to '%s' (error %d)"), szDumpPath, GetLastError() );
							szResult = szScratch;
						}
						::CloseHandle(hFile);

						_stprintf( szScratch, _T("Saved dump file to '%s'."), szDumpPath );
						szResult = szScratch;
					}
					else
					{
						_stprintf( szScratch, _T("Failed to create dump file '%s' (error %d)"), szDumpPath, GetLastError() );
						szResult = szScratch;
					}
				}
			}
			else
			{
				szResult = _T("DBGHELP.DLL too old");
			}
		}
		else
		{
			szResult = _T("DBGHELP.DLL not found");
		}

		if (szResult)
			::MessageBox( hParent, szResult, m_szAppName, MB_OK|MB_TOPMOST );

		return retval;
	}

	MiniDumper()
	{
		m_hWnd = NULL;
		::SetUnhandledExceptionFilter(TopLevelFilter);
	}
};

HWND MiniDumper::m_hWnd = NULL;
TCHAR MiniDumper::m_szAppName[_MAX_PATH] = _T("BJC2");
static MiniDumper _MiniDumper;
